5

路由拦截

第一步:路由拦截
首先在定义路由的时候就需要多添加一个自定义字段requireAuth,用于判断该路由的访问时候需要登录。如果用户已经登录,则顺利进入路由,否则就进入登录页面。

const routers=[
    {
        path:'/',
        name:'/',
        component:Index
    },
    {
        path:'/index',
        name:'index',
        meta:{
            requireAuth:true,//添加该字段,表示进入这个路由是需要登录的。
        },
        component:index
    },
    {
        path:'/login',
        name:'login',
        component:Login
    },
]
//定义完路由后,我们主要是利用vue-router提供的钩子函数beforeEach()对路由进行判断。
router.beforeEach((to,from,next)=>{
    if(to.meta.requireAuth){//判断该路由是否需要登录权限。
        if(store.state.token){//通过vuex state获取当前的token是否存在。
            next();
        }else{
            next({
                path:'/login',
                query:{redirect:to.fullPath}//将跳转的路由path作为参数,登陆成功后跳转到该路由
            })
        }
    }else{
        next();
    }
})

每个钩子方法接收三个参数:
*to:Route:即将要进入的目标路由对象。
*from:Route:当前导航正要离开的路由。
*next:Function:一定要调用该方法来resolve这个钩子。执行效果依赖next方法的调用参数。
*next();进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是confirmed。
*next(false):中断当前的导航。如果浏览器的URL改变了,那么》URL地址会重置到from路由对应的地址。(可能是用户手动或者浏览器后退按钮)
*next('/')或则next({path:'/'}):跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。
确保要调用next方法,否则钩子就不会被resolved。

其中,to.meta中是我们自定义的数据,其中就包括我们刚刚定义的requireAuth字段。通过这个字段来判断该路由是否需要登录权限。需要的话,同时当前应用不存在token,则跳转到登录页面,进行登录。登陆成功后跳转到目标路由。
登录拦截到这里就结束了码?并没有。这种方式只是简单的前端路由控制,并不能真正的阻止用户访问需要登陆权限的路由。还有一种情况是:当前token失效了,但是token依然保存在本地。这时候你去访问需要登陆权限的路由时,实际上应该让用户重新登录。
这时候就需要结合http拦截器+后端接口返回的http状态码来判断。

第二步:
要想统一处理http请求和响应,就得用axios的拦截器。通过配置http response inteceptor,当后端接口返回401,让用户重新登录。

//http request拦截器。
axios.interceptors.request.use(
    config=>{
        if(store.state.token){
            config.headers.Authorization = `token${store.state.token}`;
        }
        return config;
    },
    err=>{
        return Promise.reject(err);
    }
);
//http response拦截器
axios.interceptors.response.use(
    response=>{
        return response;
    }
    error=>{
        if(error.response){
            switch(error.response.status){
                case 401:
                    //返回401清楚token信息并跳转到登陆页面
                    store.commit(types.LOGOUT);
                    router.replace({
                        path:'/login',
                        query:{redirect:router.currentRoute.fullPath}
                    })
            }
        }
        return Promise.reject(err.response.data)//返回接口返回的错误信息。
    }
)

补充一个最全的axios的配置增加了get和post的方法封装,因为post方法传参总是需要qs来序列化请求参数。

//请求拦截、相应拦截、错误统一处理。
import axios from 'axios';
import QS from 'qs';
import store from '../store/index';
//环境的切换
if(process.env.NODE_ENV == 'development'){
    axios.defaults.baseUrl = '/api';
}else if(process.env.NODE_ENV == 'debug'){
    axios.de
    faluts.baseURL = '';
}else if(process.env.NODE_ENV == 'production'){
    axios.defaults.baseURL= 'http://api.123dailu.com/';
}
//请求超时时间
axios.defaults.timeout = 10000;
//post请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';
//请求拦截器
axios.interceptors.request.use(
    config=>{
        //每次发送请求之前判断是否存在token,如果存在,则统一在http请求的header都加上token,不用每次请求都手动添加了。
        //即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断。
        const token = store.state.toekn;
        token && (config.headers.Authorization = toekn);
        return config;
    },
    err =>{
        return Promise.error(err);
    }
)
//响应拦截器
axios.inteceptors.response.use(
    respones=>{
        if(response.status == 200){
            return Promise.resolve(response);
        }else{
            return Promise.reject(response);
        }
    },
    //服务器状态码不是200的情况
    error=>{
        if(error.response.status){
            switch(error.response.status){
                //401:未登录
                //未登录则跳转登录页面,并携带当前页面的路径。
                //在登录成功后返回当前页面,这一步需要在登录页操作。
                case 401:
                    router.replace({
                        path:'/login',
                        query:{redirect:router.currentRoute.fullPath}
                    });
                    break;
                //登录过期对用户进行提示。
                //清除本地token和清空vuex中的token对象。
                //跳转登录页面。
                case 403:
                    Toast({
                        message:'登录过期,请重新登录',
                        duration:1000,
                        forbidClick:true
                    });
                    //清除token
                    localStorage.removeItem('token');
                    store.commit('loginSuccess',null);
                    //跳转登陆页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面。
                    setTimeout(() => {                        
                        router.replace({                            
                            path: '/login',                            
                            query: { 
                                redirect: router.currentRoute.fullPath 
                            }                        
                        });                    
                    }, 1000);
                    break;
            }
            return Promise.reject(error.response);   
        }
    }
)
/** 
 * get方法,对应get请求 
 * @param {String} url [请求的url地址] 
 * @param {Object} params [请求时携带的参数] 
 */
export const get =(url, ...params)=>{    
    return new Promise((resolve, reject) =>{        
        axios.get(url, {            
            params: params        
        })        
        .then(res => {            
            resolve(res.data);        
        })        
        .catch(err => {            
            reject(err.data)        
        })    
    });
}
/** 
 * post方法,对应post请求 
 * @param {String} url [请求的url地址] 
 * @param {Object} params [请求时携带的参数] 
 */
export const post = (url,... params) =>{    
    return new Promise((resolve, reject) => {         
        axios.post(url, QS.stringify(...params))        
        .then(res => {            
            resolve(res.data);        
        })        
        .catch(err => {            
            reject(err.data)        
        })    
    });
}

使用vue-cookies+axios实现登录拦截

1.安装vue-cookies

npm install vue-cookies --save;

2.在登录接口中设定cookies

this.$http.post(global.domin+'/login/check',{
    uesrname:this.ruleForm.username,
    password:this.ruleForm.password
}).then(function(response){
    var dataObj = respones.data;
    if(dataObj.code==200){
        that.token = dataObj.data.token;
        that.$cookies.set('token',that.token,'45d');//第三个参数是过期时间。
    }
}).catch(function(error){
    console.log(error);
})

3.在main.js中设置拦截器。

//拦截器 每个接口加token
axios.interceptors.request.use(function(config){
    if(VueCookies.get('token')){
        config.headers.Authorization = String(VueCookies.get('token'))
        //具体是什么样的头,和后端匹配。
    };
    return config;
},function(error){
    return Promise.reject(error);
});
//没有就重新登录
axios.interceptors.response.use(function(response){
    // console.log(response)
    // if(response.data.code == 401) {
    //     router.replace({
    //         name: 'login'
    //     });
    // }
    return response;
},function(error){
    if(error.response.status==401){
        router.replace({
            name:'login'
        })
    }
    return Promise.reject(error)//用上面的成功的还是下面报错的根据需求。
})

Lessong
101 声望4 粉丝